﻿#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <ctype.h>


//文件分为：程序文件 和 数据文件
//源文件分为：源文件.c,目标文件.obj，可执行程序.exe。
//数据文件：文件内容不一定是程序，而是程序运行时读写的数据。

//数据文件分为：文本文件 和 二进制文件。
//区别：
//文本文件：把数据每一位当成字符，再以ASCLL码值转换成二进制形式写入文件就是文本文件。 - 直接能看懂
//二进制文件：内存存储数据的形式直接写入文件就是二进制文件，是为了持续已久的保存数据。 - 直接看，看不懂
//打开二进制文件：添加现有项，选中刚刚生成的文件，打开方式，选择二进制编译器可以进行查看存储的数据。

//int main()
//{
//	int a = 10000;
//
//	FILE* pf = fopen("data.txt", "wb");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//
//	//写文件
//	fwrite(&a, 4, 1, pf);
//
//	//关闭文件
//	fclose(pf);
//	pf = NULL;
//
//	return 0;
//}

//重点：流 和标准流
//流：是对应我们程序需要输入或输出到 各种外部设备。
//对与程序员来说去 '流'里面读写数据。

//标准流 - 程序运行默认打开的流
//stdin - 标准输入流，在⼤多数的环境中从键盘输⼊,scanf函数就是从'标准输入流'中读取数据。
//stdout - 标准输出流，⼤多数的环境中输出⾄显⽰器界⾯,printf函数就是将数据输出到'标准输出流'写数据。
//stderr - 标准错误流，⼤多数环境中输出到显⽰器界⾯。

//stdin、stdout、stderr/* 三个流的类型是： FILE* */ ，通常称为/*⽂件指针*/。
//C语⾔中，就是通过 FILE * 的⽂件指针来维护流的各种操作的。


//重点：文件指针 
//1.每个被使用的文件都会在内存开辟一个相应的文件信息区，用来存放文件相关的信息。
//2.这些信息是保存在一个结构体变量中，该结构体是由系统声明，取名 FILE。
//3.打开文件就会获得EILE*类型指针，就可以找到文件信息区，文件新区维护我们使用的文件。
//4.文件信息区 - 称为流。

//总结：
//文件指针 - 找到文件信息区 - 维护被打开的文件进行去写操作。


//一.函数学习

//1.
//fopen原型：
//FILE* fopen(const char* filename, const char* mode);
//参数：
//const char* filename：指打开文件名
//const char* mode：打开方式 - 是进行读 / 写, 是二进制的读还是二进制写。
//返回类型：
//FILE* ：返回一个FLIE结构体类型的指针

//2.
//fclose原型：
//int fclose(FILE* stream);
//参数：
//FILE* stream：FILE* 类型的指针


//知识点补充：路径
//对于写程序来说，直接写叫相对路径
//从根上写的路径 - 绝对路劲
//.和..使用，含义是当前路径上一级路径



//二.函数学习 - 顺序读写

//1.
//fputc原型：(写)
//功能：一次写一个字符。
//int fputc(int character, FILE* stream);
//参数：
//int character：参数是int，因为传字符是ASCII码值。
//FILE* stream：文件流
//含义是在文件流写数据，写字符

//int  main()
//{
//	FILE* pf = fopen("data.txt", "w");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//
//	//写字符
//	int i = 0;
//	for (i = 0; i < 26; i++)
//	{
//		fputc('a' + i, stdout);
//	}
//
//	//关闭文件
//	fclose(pf);
//	pf = NULL;
//
//	return 0;
//}


//2.
//fgetc原型：(读)
//int fgetc(FILE* stream);
//功能：一个读取一个字符
//参数：
//FILE* stream：传的是个文件流，说明在流里面读数据
//返回类型：读成功，返回字符的ASCII码值，读失败，返回EOF。
//int main()
//{
//	FILE* pf = fopen("data.txt", "r");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//
//	//读
//	//int ch = fgetc(pf);
//	//printf("%c", ch);
//
//	//ch = fgetc(pf);
//	//printf("%c", ch);
//
//	//ch = fgetc(pf);
//	//printf("%c", ch);
//
//	//ch = fgetc(pf);
//	//printf("%c", ch);
//
//	int i = 0;
//	for (i = 0; i < 26; i++)
//	{
//		int ch = fgetc(pf);
//		printf("%c", ch);
//	}
//
//	//关闭
//	fclose(pf);
//	pf = NULL;
//
//	return 0;
//}

//
//代码举例3 - 练习
//题目 写一个代码，将fufu1.txt文件的内容，拷贝一份生成fufu2.txt
//
//分析：
//1.从fufu1.txt中读取数据
//2.写到fufu2.txt的文件中

//int main()
//{
//	FILE* pf = fopen("fufu1.txt", "r");
//	if (pf == NULL)
//	{
//		perror("fopen->fufu1.txt");
//		return 1;
//	}
//	
//	FILE* ps = fopen("fufu2.txt", "w");
//	if (ps == NULL)
//	{
//		fclose(pf);
//		pf = NULL;
//		perror("fopen->fuuf2.txt");
//		return 1;
//	}
//
//	读写
//	int ch = 0;
//	while ((ch = fgetc(pf)) != EOF)
//	{
//		fputc(ch, stdout);
//	}
//
//	
//	fclose(pf);
//	fclose(ps);
//	pf = NULL;
//	ps = NULL;
//
//	return 0;
//}



//3.
//fputs原型：(写)
//int fputs(const char* str, FILE* stream);
//参数：
//const char* str：字符串
//FILE* stream：流
//含义：写一个字符串到流


//int main()
//{
//	FILE* pf = fopen("data.txt", "w");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//
//	//写字符串
//	fputs("abcdef\n", pf);
//	fputs("abcdef\n", pf);
//	fputs("abcdef\n", pf);
//
//	//
//	fclose(pf);
//	pf = NULL;
//
//	return 0;
//}



//4.
//fgets原型：(读)
//char* fgets(char* str, int num, FILE* stream);
//参数：
//char* str：从流里面读取，放到str指向的空间。
//int num：最多读多少个，num - 1个，因为最后一个会放\0, 得预留一个位置。
//FILE* stream：从流里面读取。

//int main()
//{
//	FILE* pf = fopen("data.txt", "r");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//	//
//	char arr[20] = "xxxxxxxxxxxxx";
//
//	//在屏幕上读取 - 并打印在屏幕上
//	fgets(arr, 5, stdin);
//	fputs(arr, stdout);
//
//	//
//	fclose(pf);
//	pf = NULL;;
//
//	return 0;
//}


//知识点补充：
//printf 和 scanf 都用到可变参数

//printf原型：
//int printf(const char* format, ...);//可变参数

//fprintf原型：
//int fprintf(FILE* stream, const char* format, ...);
//2个函数对比发现只有第一个参数不一样，只要会使用printf就会使用fprintf。
//含义，把格式化数据写在指向的流里面。


//struct St
//{
//	char name[20];
//	char id[20];
//	int age;
//};
//
//int main()
//{
//	struct St s = { "zhangsan","20222211058",20 };
//	FILE* pf = fopen("data.txt", "w");
//	if (pf == NULL)
//	{
//		perror("fopen1");
//		return 1;
//	}
//
//	//写
//	fprintf(pf,"%s %s %d", s.name, s.id, s.age);
//
//	//
//	fclose(pf);
//	pf = NULL;
//
//	return 0;
//}


//7.
//scanf原型：(读)
//int scanf(const char* format, ...);//可变参数
//
//fscanf原型：
//int fscanf(FILE* stream, const char* format, ...);
//2个函数对比发现只有第一个参数不一样，只要会使用scanf就会使用fscanf。
//含义：从流里面读取格式化数据

//struct St
//{
//	char name[20];
//	int age;
//	float score;
//};
//int main()
//{
//	struct St s = { 0 };
//	FILE* pf = fopen("data.txt", "r");
//	if (pf == NULL)
//	{
//		perror("fopen");
//		return 1;
//	}
//
//	fscanf(pf, "%s %d %f\n", s.name, &(s.age), &(s.score));
//	fprintf(stdout, "%s %d %.1f\n", s.name, s.age, s.score);
//
//	fclose(pf);
//	pf = NULL;
//
//	return 0;
//}

//注意打印的时候可以.f控制长度，但读的时候不行。
//总结：
//1.fgetc和fputc，fgets和fputs，fscanf和fpirntf 这些函数都是以文本信息写到文件，直接能看懂的，读相反。
//2.这些函数只是针对另一种输入输出的手段。

